package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constans.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.learning.constans.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author mxl
 * @since 2024-06-28
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final RedisTemplate redisTemplate;

    private final UserClient userClient;

    @Override
    public PointsBoardVO queryPointsBoardList(PointsBoardQuery query) {
        // 1. 获取用户id
        Long  user = UserContext.getUser();
        // 2. 判断查当前赛季还是历史赛季
        boolean isCurrent = query.getSeason() == null || query.getSeason() == 0;

        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX+format;

        // 3. 查询我的排名和积分
        Long season = query.getSeason();
        PointsBoard pointsBoard = isCurrent? queryMyCurrentBoard(user, key): queryHistoryBoard(user, season);

        // 4. 分页查询赛季列表根据query season 判断是否为当前赛季
        List<PointsBoard> pointsBoardList = isCurrent? queryMyCurrentBoard(key, query.getPageSize(), query.getPageNo()): queryHistoryBoard(season, query.getPageSize(), query.getPageNo());

        if(CollUtils.isEmpty(pointsBoardList)){
            return null;
        }

        // 5. 封装用户信息
        Set<Long> collect = pointsBoardList.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userDTOS = userClient.queryUserByIds(collect);
        if(CollUtils.isEmpty(userDTOS)){
            throw new BizIllegalException("用户信息不存在");
        }

        Map<Long, String> userNames = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        PointsBoardVO vo = new PointsBoardVO();
        vo.setRank(pointsBoard.getRank());
        vo.setPoints(pointsBoard.getPoints());
        List<PointsBoardItemVO> items = new ArrayList<>();
        for (PointsBoard board : pointsBoardList) {
            PointsBoardItemVO item = new PointsBoardItemVO();
            String userName = userNames.get(board.getUserId());
            if(userName != null){
                item.setName(userName);
            }
            item.setRank(board.getRank());
            item.setPoints(board.getPoints());
            items.add(item);
        }
        vo.setBoardList(items);
        return vo;
    }

    /**
     * 分页查询历史榜单信息
     * @param season 赛季
     * @param pageSize 页面大小
     * @param pageNo 页号
     * @return 历史赛季页面信息
     */
    private List<PointsBoard> queryHistoryBoard(Long season, Integer pageSize, Integer pageNo) {
        // 1. 获取表名
        String tableName = POINTS_BOARD_TABLE_PREFIX + season;
        TableInfoContext.setInfo(tableName);

        // 2. 查询数据库
        Page<PointsBoard> page = this.lambdaQuery()
                .orderByAsc(PointsBoard::getId)
                .page(new Page<PointsBoard>(pageNo, pageSize));
        List<PointsBoard> records = page.getRecords();
        if(CollUtils.isEmpty(records)){
            return CollUtils.emptyList();
        }

        records.stream().forEach(record->{      // 设置赛季和 排名字段
            record.setRank(record.getId().intValue());
            record.setSeason(season.intValue());
        });
        TableInfoContext.remove();

        // 3. 返回结果
        return records;
    }

    /**
     * 分页查询积分榜单信息
     * @param key redis榜单键
     * @param pageSize 页面大小
     * @param pageNo 页面号
     * @return
     */
    public List<PointsBoard> queryMyCurrentBoard(String key, Integer pageSize, Integer pageNo) {
        // 1. 查询库
        int start = (pageNo - 1) * pageSize;
        Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, start + pageSize - 1);
        // 2. 封装
        if(CollUtils.isEmpty(set)){
            return CollUtils.emptyList();
        }

        int rank = 1+start;
        List<PointsBoard> pointsBoardList = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> stringTypedTuple : set) {
            String value = stringTypedTuple.getValue();
            Double score = stringTypedTuple.getScore();
            if(StringUtils.isBlank(value) || score == null){
                continue;
            }
            PointsBoard pointsBoard = new PointsBoard();
            pointsBoard.setUserId(Long.valueOf(pageSize));
            pointsBoard.setPoints(score.intValue());
            pointsBoard.setRank(rank++);
            pointsBoardList.add(pointsBoard);
        }

        return pointsBoardList;
    }

    /**
     * 查询历史榜单信息
     * @param user 用户id
     * @param season 赛季id
     * @return 历史赛季的我的积分以及排名
     */
    private PointsBoard queryHistoryBoard(Long user, Long season) {
        // 1. 获取表名
        String tableName = POINTS_BOARD_TABLE_PREFIX + season;
        TableInfoContext.setInfo(tableName);

        // 2. 查询数据
        PointsBoard pointsBoard = this.lambdaQuery()
                .eq(PointsBoard::getUserId, user)
                .one();
        TableInfoContext.remove();
        // 3. 封装结果

        pointsBoard.setRank(pointsBoard.getId().intValue());
        pointsBoard.setSeason(season.intValue());
        return pointsBoard;
    }

    /**
     * 查询我的积分以及排名
     * @param user 用户
     * @param key redis 赛季 键
     * @return
     */
    private PointsBoard queryMyCurrentBoard(Long user, String key) {
        // 获取分值
        Double score = redisTemplate.opsForZSet().score(key, user.toString());
        // 获取排名
        Long rank = redisTemplate.opsForZSet().reverseRank(key, user.toString());

        // 封装
        PointsBoard pointsBoard = new PointsBoard();

        pointsBoard.setRank(rank == null? 0: rank.intValue()+1);
        pointsBoard.setPoints(score == null? 0: score.intValue());
        return pointsBoard;
    }
}
